#include <stdlib.h>
#include <iostream>
#include <string>
#include <time.h>
#include <sstream>
#include <string>
#include <math.h>
#include <fstream>

#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
#include "SDL/SDL_ttf.h"
#include "SDL/SDL_mixer.h"

#include "tiles.h"

using namespace std;

//-------------CONST-------------
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;
const int SCREEN_BPP = 16;
const int FPS = 60;
const int TILE_SIZE = 20;
const int PLAYER_SIZE = 35;
//-------------VARIABLE-------------
double playerX;
double acelX;
double playerY;
double acelY;
int YLocation;
bool playerAlive;
bool playerAcelPressed;
int frame;
int tmrframe;
int blinkScreen;
int timerToBlinkScreen;
int playerLives;
int currentLevel;
int lastLevel;
bool gameOver;
int timeLeftLevel;
bool levelFinished;
int fadeTemp;
Tiles *mapTiles[900];
char infoScreen[50];
//-------------SDL-------------
SDL_Color textColor = {255, 255, 255};
SDL_Event event;
TTF_Font *font1 = NULL;
TTF_Font *font2 = NULL;
SDL_Surface *screen = NULL;
SDL_Surface *message = NULL;
SDL_Surface *backgroundImg = NULL;
SDL_Surface *shipImg = NULL;
SDL_Surface *menuImg = NULL;
SDL_Surface *shipFireImg = NULL;
SDL_Surface *lifeImg = NULL;
SDL_Surface *wall0TileImg = NULL;
SDL_Surface *wall1TileImg = NULL;
SDL_Surface *wall2TileImg = NULL;
SDL_Surface *wall3TileImg = NULL;
SDL_Surface *wall4TileImg = NULL;
SDL_Surface *warpZoneTileImg = NULL;

//=====================Zera todas as variaveis=====================
void resetGameStartAttribs() {
	playerX = TILE_SIZE;
	playerY = TILE_SIZE;
	acelX = 0;
	acelY = 0;
	timeLeftLevel = 60;
	playerAlive = true;
	playerAcelPressed = false;
	frame = 0;
	tmrframe;
	blinkScreen = false;
	timerToBlinkScreen = 0;
	playerLives = 2;
	currentLevel = 1;
	lastLevel = 16;
	gameOver = false;
	levelFinished = false;
	fadeTemp = 1;
}

//=====================FPS CONTROL=====================
void waitframe(void) {
	static Uint32 next_tick = 0;
	Uint32 this_tick;

	this_tick = SDL_GetTicks();
	if(this_tick < next_tick) SDL_Delay(next_tick-this_tick);
	next_tick = this_tick + (1000/FPS);
}

//=====================LOAD IMAGES=====================
SDL_Surface *load_image( std::string filename ) {
    SDL_Surface* loadedImage = NULL;
    SDL_Surface* optimizedImage = NULL;

	loadedImage = IMG_Load(filename.c_str());

	if(loadedImage != NULL) {
		SDL_SetColorKey(loadedImage, SDL_SRCCOLORKEY, SDL_MapRGB(loadedImage->format, 255, 0, 255));
	    optimizedImage = SDL_DisplayFormat(loadedImage);
	    SDL_FreeSurface(loadedImage);
	} else {
		printf("impossible to load image %s\n", filename.c_str());
    }

    return optimizedImage;
}

//=====================DRAW IMAGE AT X,Y=====================
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* destination) {
    SDL_Rect offset;
    offset.x = x;
    offset.y = y;

    SDL_BlitSurface(source, NULL, destination, &offset);
}

//=====================INIT ALL=====================
bool init() {
    if(SDL_Init(SDL_INIT_EVERYTHING) == -1) return false;

    screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);

    if(screen == NULL) return false;
    if(TTF_Init() == -1) return false;

    SDL_WM_SetCaption("WarpZone", NULL);

    return true;
}

void loadBackground() {
	int backgroundImgRand = rand() % 5 + 1;
	std::stringstream backgroundToLoad;
	backgroundToLoad << "arqs/bg" << backgroundImgRand << ".png";
	cout << backgroundToLoad.str() << "\n";
	backgroundImg = load_image(backgroundToLoad.str());
}

//=====================LOAD RESOURCES=====================
bool loadResources() {
    shipImg = load_image("arqs/player.png");
	menuImg = load_image("arqs/menu.png");
	loadBackground();

	wall0TileImg = load_image("arqs/wz.png");
	wall1TileImg = load_image("arqs/wall1.png");
	wall2TileImg = load_image("arqs/wall2.png");
	wall3TileImg = load_image("arqs/wall3.png");
	wall4TileImg = load_image("arqs/wall4.png");
	warpZoneTileImg = load_image("arqs/wz2.png");
	lifeImg = load_image("arqs/heart.png");

	//shipFireImg = load_image("arqs/playerFire.bmp");
	//Global explo = LoadAnimImage("..\arqs\explo.png",40,30,0,6)
	//Global lasersword = LoadSound("..\arqs\lasersword.wav")
	//Global lasersword2 = LoadSound("..\arqs\lasersword2.wav")

    font1 = TTF_OpenFont("arqs/font.ttf", TILE_SIZE);
	font2 = TTF_OpenFont("arqs/font.ttf", TILE_SIZE + TILE_SIZE/4);

    if(font1 == NULL || font2 == NULL) return false;

    return true;
}

//=====================CLEAN EVERYTHING=====================
void cleanUp() {
    SDL_FreeSurface(shipImg);
    SDL_FreeSurface(backgroundImg);
	SDL_FreeSurface(menuImg);
	SDL_FreeSurface(wall0TileImg);
	SDL_FreeSurface(wall1TileImg);
	SDL_FreeSurface(wall2TileImg);
	SDL_FreeSurface(wall3TileImg);
	SDL_FreeSurface(wall4TileImg);
	SDL_FreeSurface(warpZoneTileImg);
	SDL_FreeSurface(lifeImg);
    
    TTF_CloseFont(font1);
	TTF_CloseFont(font2);
    
    TTF_Quit();
    SDL_Quit();
}

//=====================LOAD MAP (based on currentLevel attribute)=====================
void loadMap() {
	string currentLine;
	ifstream infile;
	std::stringstream mapToLoad;//open map to load
	
	mapToLoad << "maps/" << currentLevel << ".map";
	//cout << mapToLoad.str() << "\n";
	infile.open(mapToLoad.str().c_str());

	int num = 0;
	int bmp_x = 0;
	int bmp_y = 0;
	for (int i = 0; i < 900; i++) {
		mapTiles[i] = new Tiles();
		mapTiles[i]->tile_var = 0;
		getline(infile, currentLine);
		mapTiles[i]->number = num;

		if(!currentLine.empty() && currentLine.size() > 1) {
			mapTiles[i]->tileImageNum = atoi(currentLine.c_str());
		} else {
			mapTiles[i]->tileImageNum = 99;
		}

		mapTiles[i]->x = bmp_x;
		mapTiles[i]->y = bmp_y;
		
		bmp_x = bmp_x + TILE_SIZE;
		if(bmp_x == SCREEN_HEIGHT) {
			bmp_x = 0;
			bmp_y = bmp_y + TILE_SIZE;
		}
		num++;
	}
	
	num = 0;
	for (int j = 901; j < 1800; j++) {
		getline(infile, currentLine);
		mapTiles[num]->tile_type = atoi(currentLine.c_str());
		num++;
	}
	infile.close();
}

//=====================DRAW MAP AT X,Y=====================
void drawMap(int posX, int posY) {
	for (int i = 0; i < 900; i++) {
		switch(mapTiles[i]->tileImageNum) {
			case 0:
				apply_surface(mapTiles[i]->x + posX, mapTiles[i]->y + posY, wall0TileImg, screen);
				break;
			case 1:
				apply_surface(mapTiles[i]->x + posX, mapTiles[i]->y + posY, wall1TileImg, screen);
				break;
			case 2:
				apply_surface(mapTiles[i]->x + posX, mapTiles[i]->y + posY, wall2TileImg, screen);
				break;
			case 3:
				apply_surface(mapTiles[i]->x + posX, mapTiles[i]->y + posY, wall3TileImg, screen);
				break;
			case 4:
				apply_surface(mapTiles[i]->x + posX, mapTiles[i]->y + posY, wall4TileImg, screen);
				break;
			//case 5:
				//apply_surface(mapTiles[i]->x, mapTiles[i]->y, warpZoneTileImg, screen);
				//break;
		}
	}
}

//=====================CHECK MAP/PLAYER COLLISION=====================
int checkCollision() {
	int temp = 0;//tile_type colliding

	for(int i = 0; i < 900; i++) {
		if(mapTiles[i]->tile_type > 0) {
			if(playerY + PLAYER_SIZE > mapTiles[i]->y && playerY < mapTiles[i]->y + TILE_SIZE) {
				if(playerX + PLAYER_SIZE > mapTiles[i]->x && playerX < mapTiles[i]->x + TILE_SIZE) {
					temp = mapTiles[i]->tile_type;
					break;
				}
			}
		}
	}		
	return temp;
}

//=====================SHOW GAME INFO (AND DEBUG)=====================
void showInfos() {
	//---------------HEADER---------------
	sprintf(infoScreen, "%d", currentLevel);
	message = TTF_RenderText_Solid(font2, infoScreen, textColor);
    apply_surface(693, 244, message, screen);
	sprintf(infoScreen, "%d", timeLeftLevel);
	message = TTF_RenderText_Solid(font2, infoScreen, textColor);
    apply_surface(730, 280, message, screen);
	sprintf(infoScreen, "%d", playerLives);
	message = TTF_RenderText_Solid(font2, infoScreen, textColor);
    apply_surface(733, 315, message, screen);

	//------------------------------
	if (!playerAlive) {
		if (playerLives > -1) {
			message = TTF_RenderText_Solid(font2, "Press SPACE to try again !", textColor);
			apply_surface(230, 120, message, screen);
		} else {
			message = TTF_RenderText_Solid(font2, "GAME OVER", textColor);
			apply_surface(230, 100, message, screen);
			message = TTF_RenderText_Solid(font2, "Thanks for playing", textColor);
			apply_surface(230, 140, message, screen);
		}
	}
	/**DEBUG**/
	message = TTF_RenderText_Solid(font1, "DEBUG:", textColor);
    apply_surface(10, 150, message, screen);
	sprintf(infoScreen, "%g", playerX);
	message = TTF_RenderText_Solid(font1, infoScreen, textColor);
    apply_surface(10, 150, message, screen);
	sprintf(infoScreen, "%g", playerY);
	message = TTF_RenderText_Solid(font1, infoScreen, textColor);
    apply_surface(10, 190, message, screen);
	sprintf(infoScreen, "%g", acelX);
	message = TTF_RenderText_Solid(font1, infoScreen, textColor);
    apply_surface(10, 220, message, screen);
	sprintf(infoScreen, "%g", acelY);
	message = TTF_RenderText_Solid(font1, infoScreen, textColor);
    apply_surface(10, 250, message, screen);
}

//=====================RESET ATTRIBUTES (restart level)=====================
void resetPlayerDiedAttribs() {
	playerX = TILE_SIZE;
	playerY = TILE_SIZE;
	acelX = 0;
	acelY = 0;
	playerAlive = true;
	timeLeftLevel = 60;
}

//=====================MAIN=====================
int main(int argc, char* args[]) {

	FILE * ctt = fopen("erros.txt", "w");
    freopen("errors.txt", "w", stdout);

	resetGameStartAttribs();
	srand(time(NULL));

    bool quit = false;
    if(init() == false) return 1;
    if(loadResources() == false) return 1;
    if(SDL_Flip(screen) == -1) return 1;

	loadMap();
	
    Uint32 start = 0;
    bool running = true;
    start = SDL_GetTicks();

	int collisionType = 0;

	while(!quit) {
    	waitframe();
		Uint8 *keystates = SDL_GetKeyState(NULL);
		while(SDL_PollEvent(&event)) {
            if(event.type == SDL_QUIT) quit = true;
        }
		if(keystates[SDLK_ESCAPE]) quit = true; //Quit game

		//======================DESENHANDO======================
		apply_surface(0, 0, backgroundImg, screen);//background
		apply_surface(600, 0, menuImg, screen);//menu (right)

		if (!levelFinished) {
			//Level timer (counts seconds)
		    if(running == true) {
		        int time = SDL_GetTicks() - start;
				//cout << "Timer: " << SDL_GetTicks() - start << "\n";
				if(time > 1000) {
					timeLeftLevel--;
					start += 1000;
				}
		    }

			if(currentLevel == 10) {//blink the map
				// blinkScreen();
			} else {
				blinkScreen = false;
			}

			drawMap(0, 0);//map
		
			if(playerAcelPressed) {//draw ship fire
				//apply_surface(x + 7.5, y + 25, shipFireImg, screen);
			}
		
			//======================INPUT HANDLE======================
			if(playerAlive) {
				if(keystates[SDLK_SPACE]) {
					acelY = acelY - 0.03;
					playerAcelPressed = true;
				}
				if(keystates[SDLK_LEFT]) {
					acelX = acelX - 0.05;
					playerAcelPressed = true;
				}
				if(keystates[SDLK_RIGHT]) {
					acelX = acelX + 0.05;
					playerAcelPressed = true;
				}

				if(acelX > 0) {
					acelX = acelX - 0.01;
				}
				if(acelX < 0) {
					acelX = acelX + 0.01;
				}
				if(acelY < 0) {
					acelY = acelY + 0.01;
				}

				acelY = acelY + 0.01;
				playerX = playerX + acelX;
				playerY = playerY + acelY;

				apply_surface(playerX, playerY, shipImg, screen);//Draw player
			} else {
				if(keystates[SDLK_SPACE]) {
					resetPlayerDiedAttribs();
				}
				//explo() DESENHAR EXPLOSÃO ***********************************************
				if (playerLives == -1) gameOver = true;
			}
		
			if (playerAlive) {
				collisionType = checkCollision();
				if(collisionType == 1) { //Wall
					playerAlive = false;
					playerAcelPressed = false;
					playerLives--;
				} else if(collisionType == 2) { //Warp Zone normal
					currentLevel++;
					levelFinished = true;
				} else if(collisionType == 3) { //Warp type 3. Pass 2 levels
					currentLevel += 2;
					levelFinished = true;
				} else if(collisionType == 4) { //Warp type 4. Return 1 level
					currentLevel -= 2;
					levelFinished = true;
				} else if(collisionType == 9) { //Extra life! (only one by level)
					for (int i = 0; i < 900; i++) {
						if (mapTiles[i]->tile_type == 9) {
							playerLives++;
							mapTiles[i]->tile_type = 0;
						}
					}
				}
			}
		} else {
			if (fadeTemp < (SCREEN_HEIGHT + TILE_SIZE)) {
				fadeTemp += TILE_SIZE/2;
				drawMap(0, fadeTemp);
			} else {
				loadMap();
				levelFinished = false;
				resetPlayerDiedAttribs();
				fadeTemp = 1;
				loadBackground();
			}
		}

		showInfos();
		if(SDL_Flip(screen) == -1) return 1;
	}

    cleanUp;
	return 0;
}
